package snooker.scoreboard;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import android.os.Environment;
import android.util.Log;

public class FileHandler {
	private static final String LOG_TAG = "FileHandler";
	private static final String ROOT_FOLDER = "/snooker_results";
	public static final String HEADER = "Player,Frame,Type,Break,Ball,Points,Time,wasLucky";
	public static final String OLD_HEADER = "Player,Frame,Type,Break,Ball,Points,Time";
	
	public static boolean saveMatch() throws SnookerFileException {
		Match match = Match.getCurrentMatch();
		
		boolean result = false;
		try {
			File root = Environment.getExternalStorageDirectory();
			Log.i(LOG_TAG, "root=" + root);
			if (root.canWrite()){
				String rootFolderPath = root + ROOT_FOLDER;
				Log.i(LOG_TAG, "rootFolderPath=" + rootFolderPath);
				File rootFolder = new File(rootFolderPath);
				if (!rootFolder.isDirectory()) {
					Log.i(LOG_TAG, "Does not exists " + rootFolderPath);
					result = rootFolder.mkdir();
					Log.i(LOG_TAG, "Create result=" + result);
				}
				String name = match.getFileName();
				File gpxfile = new File(rootFolderPath, name);
				FileWriter gpxwriter = new FileWriter(gpxfile);
				BufferedWriter out = new BufferedWriter(gpxwriter);
				out.write(HEADER);
				out.write("\n");
				for (Frame f: match.getFrames()) {
					Log.i(LOG_TAG, "Saving frame: " + f.getFrameId());
					Log.i(LOG_TAG, "Saving history size: " + f.getHistory().getHistory().size());
					for (FrameState state: f.getHistory().getHistory()) {
						Log.i(LOG_TAG, "Saving state: " + state.toString());
						out.write("" +
								(state.getPlayerId() == Match.PLAYER1 ? 
										match.getPlayer1Name() : 
											match.getPlayer2Name()) + "," +
								state.getFrameId() + "," + 
								state.getType() + "," + 
								(state.getBreakId() == 0 ? "" : state.getBreakId()) + "," + 
								state.getBall() + "," +
								state.getScore() + "," + 
								state.getEventTimeString() + "," + 
								(state.isLuckyShot() ? "*" : " ") + "\n");
					}
				}
				out.close();
				result = true;
				//saveMatchStatistics(match);
			} else {
				 Log.e(LOG_TAG, "Could not save file to " + root);
				 throw new SnookerFileException(SnookerFileException.SNOOKER_CANT_SAVE); 
			}
		} catch (IOException e) {
			e.printStackTrace();
			Log.e(LOG_TAG, "Could not write file root folder does not exists");
			throw new SnookerFileException(SnookerFileException.SNOOKER_CANT_SAVE); 
		}
		return result;
	}
	
	public static void loadMatch(String fileName) throws SnookerFileException {
		try {
			File root = Environment.getExternalStorageDirectory();
			Log.i(LOG_TAG, "root=" + root);
			if (root.canRead()){
				String rootFolderPath = root + ROOT_FOLDER;
				Log.i(LOG_TAG, "rootFolderPath=" + rootFolderPath);
				File rootFolder = new File(rootFolderPath);
				if (!rootFolder.exists()) {
					Log.i(LOG_TAG, "Does not exists " + rootFolderPath);
					throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_ACCESSABLE);
				}
				File gpxfile = new File(rootFolderPath, fileName);
				if (!gpxfile.exists()) {
					Log.i(LOG_TAG, "Does not exists " + fileName);
					throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_EXISTS);
				}
				if (!gpxfile.canRead()) {
					Log.i(LOG_TAG, "Can not read file " + fileName);
					throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_READABLE);
				}
				FileReader gpxreader = new FileReader(gpxfile);
				BufferedReader in = new BufferedReader(gpxreader);
				String line = in.readLine();
				if (!HEADER.equals(line) && !OLD_HEADER.equals(line)) {
					throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_FORMAT_ERROR, 
							"Header Error! Does not match " + HEADER);
				}
				String player1Name = null;
				String player2Name = null;
				int actFrameId = -1;
				
				int playerId = Match.PLAYER1;
				int frameId;
				Type type;
				int breakId = 0;
				Ball ball;
				int score;
				int player1Score = 0;
				int player2Score = 0;
				long eventOffSet;
				
				List<FrameState> frameStates = new LinkedList<FrameState>();
				List<Frame> frames = new LinkedList<Frame>();
				Log.i(LOG_TAG, "10 ACT FRAME ID=" + actFrameId);
				while ((line = in.readLine()) != null) {
					Score state = parseFrameState(line);
					if (player1Name == null) {
						playerId = Match.PLAYER1;
						player1Name = state.playerS;
					} else if (!player1Name.equals(state.playerS) && player2Name == null) {
						playerId = Match.PLAYER2;
						player2Name = state.playerS;
					} else if (player1Name.equals(state.playerS)) {
						playerId = Match.PLAYER1;
					} else {
						playerId = Match.PLAYER2;
					}
					frameId = Integer.parseInt(state.frameS);
					if (actFrameId == -1) {
						Log.i(LOG_TAG, "21 ACT FRAME ID=" + actFrameId);
						actFrameId = frameId;
						Log.i(LOG_TAG, "22 ACT FRAME ID=" + actFrameId);
					}
					if (actFrameId != frameId) {
						Log.i(LOG_TAG, "30 ACT FRAME ID=" + actFrameId);
						List<FrameState> f = frameStates;
						frames.add(new Frame(frames.size() + 1, 0, player1Score, 
								player2Score, breakId, playerId, 
								new FrameHistory(f)));
						frameStates = new LinkedList<FrameState>();
					}
					
					type = Type.valueOf(state.typeS);
					breakId = "".equals(state.breakS) ? 0 : Integer.parseInt(state.breakS);
					ball = Ball.valueOf(state.ballS);
					score = Integer.parseInt(state.pointS);
					String[] s = state.timeS.split(":");
					eventOffSet = (Integer.parseInt(s[0]) * 60 + Integer.parseInt(s[1])) * 1000;
					if (actFrameId != frameId) {
						player1Score = 0;
						player2Score = 0;
					}
					switch (playerId) {
					case Match.PLAYER1:
						if (type.equals(Type.Pot) ||  type.equals(Type.Freeball)) {
							player1Score += score;
						} else {
							player2Score += score;
						}
						break;
					case Match.PLAYER2:
						if (type.equals(Type.Pot) ||  type.equals(Type.Freeball)) {
							player2Score += score;
						} else {
							player1Score += score;
						}
						break;
					}
					FrameState fs = new FrameState(
							playerId, frameId, type, breakId, ball, score, 
							player1Score, player2Score, 0, eventOffSet, "*".equals(state.wasLuckyS));
					Log.i(LOG_TAG, "Load frame state: " + fs.toString());
					frameStates.add(fs);
					if (actFrameId != frameId) {
						Log.i(LOG_TAG, "90 ACT FRAME ID=" + actFrameId);
						actFrameId = frameId;
						Log.i(LOG_TAG, "91 ACT FRAME ID=" + actFrameId);
					}
				}
				in.close();
				if (player1Name == null) {
					player1Name = "Player1";
				}
				if (player2Name == null) {
					player2Name = "Player2";
				}
				frames.add(new Frame(frames.size() + 1, 0, player1Score, 
						player2Score, 0, 1, 
						new FrameHistory(frameStates)));
				if (frames.size() > 0) {
					Match match = Match.getCurrentMatch();
					match.setPlayer1Name(player1Name);
					match.setPlayer2Name(player2Name);
					match.newMatch(fileName, frames);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			Log.e(LOG_TAG, "Could not write file " + e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_FORMAT_ERROR, e.getMessage());
		}
	}
	
//	private static final String T = ",";
//	private static final String NL = "\r\n";
//	private static boolean saveMatchStatistics(Match match) throws SnookerFileException {
//		boolean result = false;
//		try {
//			File root = Environment.getExternalStorageDirectory();
//			Log.i(LOG_TAG, "root=" + root);
//			if (root.canWrite()){
//				String rootFolderPath = root + ROOT_FOLDER;
//				Log.i(LOG_TAG, "rootFolderPath=" + rootFolderPath);
//				File rootFolder = new File(rootFolderPath);
//				if (!rootFolder.isDirectory()) {
//					Log.i(LOG_TAG, "Does not exists " + rootFolderPath);
//					result = rootFolder.mkdir();
//					Log.i(LOG_TAG, "Create result=" + result);
//				}
//				String name = match.getFileName().replaceAll("match", "match_stat");
//				File gpxfile = new File(rootFolderPath, name);
//				FileWriter gpxwriter = new FileWriter(gpxfile);
//				BufferedWriter out = new BufferedWriter(gpxwriter);
//				List<StatisticRecord> srl = Statistics.getMatchStatistics(match);
//				for (StatisticRecord sr: srl) {
//						out.write(sr.getName() + T + sr.getValue1() + T + 
//								sr.getValue2() + NL);
//				}
//				boolean isfirstrow = true;
//				for (Frame frame: match.getFrames()) {
//					List<StatisticRecord> lsr = Statistics.getFrameStatistics(
//							Match.getCurrentMatch(), frame.getFrameId());
//					Log.i(LOG_TAG, "frameid=" + frame.getFrameId());
//					if (lsr == null) {
//						continue;
//					}
//					if (isfirstrow) {
//						isfirstrow = false;
//						out.write(NL);
//						for (StatisticRecord sr: lsr) {
//							out.write(sr.getName() + T);
//						}
//					}
//					out.write(NL + "Frame " + frame.getFrameId() + "." + NL);
//					for (StatisticRecord sr: lsr) {
//						out.write(sr.getValue1() + T);
//					}
//					out.write(NL);
//					for (StatisticRecord sr: lsr) {
//						out.write(sr.getValue2() + T);
//					}
//				}
//				out.close();
//				result = true;
//			} else {
//				 Log.e(LOG_TAG, "Could not save file to " + root);
//				 throw new SnookerFileException(SnookerFileException.SNOOKER_CANT_SAVE); 
//			}
//		} catch (IOException e) {
//			e.printStackTrace();
//			Log.e(LOG_TAG, "Could not write file root folder does not exists");
//			throw new SnookerFileException(SnookerFileException.SNOOKER_CANT_SAVE); 
//		}
//		return result;
//	}
	
	public static String[] getFileNames() throws SnookerFileException {
		String[] fileList = new String[0];
		try {
			File root = Environment.getExternalStorageDirectory();
			Log.i(LOG_TAG, "root=" + root);
			if (root.canRead()){
				String rootFolderPath = root + ROOT_FOLDER;
				File folder = new File(rootFolderPath);
				Log.i(LOG_TAG, "root=" + rootFolderPath);
				if (folder.exists() && folder.canRead()) {
					Log.i(LOG_TAG, "root=" + rootFolderPath);
					FilenameFilter filter = new FilenameFilter() { 
						public boolean accept(File dir, String name) { 
							return name.endsWith(".csv") && !name.contains("match_stat"); 
						} 
					}; 
					fileList = folder.list(filter);
					if (fileList == null) {
						fileList = new String[0];
					}
				} else {
					throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_ACCESSABLE);
				}
			} else {
				throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_ACCESSABLE);
			}
		} catch (Exception e) {
			e.printStackTrace();
			Log.e(LOG_TAG, "Could not write file root folder does not exists");
			throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_NOT_ACCESSABLE);
		}
		return fileList;
	}
	
	private static Score parseFrameState(String line) throws SnookerFileException {
		//"Player,Frame,Type,Break,Ball,Points,Time"
		String[] cells = line.split(",");
		Score state = null;
		if (cells.length <= 1) {
			return null;
		}
		if (cells.length == 8 || cells.length == 7) {
			String playerS = cells[0];
			String frameS = cells[1];
			String typeS = cells[2];
			String breakS = cells[3];
			String ballS = cells[4];
			String pointS = cells[5];
			String timeS = cells[6];
			String wasLuckyS = " ";
			if (cells.length == 8) {
				wasLuckyS = cells[7];
			}
			
			state = new Score(playerS, frameS, typeS, breakS, ballS, pointS, timeS, wasLuckyS);
		} else {
			throw new SnookerFileException(SnookerFileException.SNOOKER_FILE_FORMAT_ERROR);
		}
		return state;
	}

	private static class Score {
		String playerS;
		String frameS;
		String typeS;
		String breakS;
		String ballS;
		String pointS;
		String timeS;
		String wasLuckyS;
		
		public Score(String playerS, String frameS, String typeS,
				String breakS, String ballS, String pointS, String timeS, String wasLuckyS) {
			super();
			this.playerS = playerS;
			this.frameS = frameS;
			this.typeS = typeS;
			this.breakS = breakS;
			this.ballS = ballS;
			this.pointS = pointS;
			this.timeS = timeS;
			this.wasLuckyS = wasLuckyS;
		}
	}
}
